const RENDER_TO_DOM = Symbol('render to dom');
export class Component {
  constructor() {
    this.props = Object.create(null);
    this.children = [];
    this._root = null;
    this._range = null;
  }

  setAttribute(name, value) {
    this.props[name] = value;
  }

  appendChild(component) {
    this.children.push(component);
  }

  [RENDER_TO_DOM](range) {
    this._range = range;
    this._vdom = this.vdom;
    this._vdom[RENDER_TO_DOM](range);
  }

  // rerender() {
  //   let oldRange = this._range;

  //   let range = document.createRange();
  //   range.setStart(oldRange.startContainer, oldRange.startOffset);
  //   range.setEnd(oldRange.startContainer, oldRange.startOffset);
  //   this[RENDER_TO_DOM](range);

  //   oldRange.setStart(range.endContainer, range.endOffset);
  //   oldRange.deleteContents();
  // }

  update() {
    /**
     * 判断两个是否相同
     * @param {*} oldNode
     * @param {*} newNode
     */
    const isSameNode = (oldNode, newNode) => {
      // 类型不同
      if (oldNode.type !== newNode.type) return false;

      // 属性不同
      for (let name in newNode.props) {
        if (newNode[name] !== oldNode[name]) {
          return false;
        }
      }

      // 属性长度不同，增加或减少
      if (
        Object.keys(oldNode.props).length > Object.keys(newNode.props).length
      ) {
        return false;
      }

      // 文本节点，比较文本内容
      if (newNode.type === '#text') {
        if (newNode.content !== oldNode.content) {
          return false;
        }
      }

      return true;
    };

    /**
     * 更新node，如果node不同就更新覆盖
     * @param {*} oldNode
     * @param {*} newNode
     */
    const update = (oldNode, newNode) => {
      if (!isSameNode(oldNode, newNode)) {
        newNode[RENDER_TO_DOM](oldNode._range);
        return;
      }

      newNode._range = oldNode._range;

      const newVChildren = newNode.vchildren;
      const oldVChildren = oldNode.vchildren;

      if (!newVChildren || !newVChildren.length) {
        return;
      }

      let tailRange = oldVChildren[oldVChildren.length - 1]._range;

      for (let i = 0, length = newVChildren.length; i < length; i++) {
        let newChild = newVChildren[i];
        let oldChild = oldVChildren[i];

        if (i < oldVChildren.length) {
          try {
            update(oldChild, newChild);
          } catch (error) {
            console.log(oldChild);
            console.log(newChild);
          }
        } else {
          let range = document.createRange();
          range.setStart(tailRange.endContainer, tailRange.endOffset);
          range.setEnd(tailRange.endContainer, tailRange.endOffset);
          newChild[RENDER_TO_DOM](range);
          tailRange = range;
        }
      }
    };

    let vdom = this.vdom;
    update(this._vdom, vdom);
    this._vdom = vdom;
  }

  setState(newState) {
    if (this.state === null || typeof this.state !== 'object') {
      this.state = newState;
      this.update();
      return;
    }

    const merge = (oldState, newState) => {
      for (let key in newState) {
        if (oldState[key] === null || typeof oldState[key] !== 'object') {
          oldState[key] = newState[key];
        } else {
          merge(oldState[key], newState[key]);
        }
      }
    };

    merge(this.state, newState);
    this.update();
  }

  get vdom() {
    // render 会通过 createElement 返回 component
    return this.render().vdom;
  }

  // get vchildren() {
  //   return this.children.map(child.vdom);
  // }
}

class ElementWrapper extends Component {
  constructor(type) {
    super(type);
    this.type = type;
  }
  /**
   * 
   * @param {*} name 
   * @param {*} value 
 
  setAttribute(name, value) {
    if (name.match(/^on([\s\S]+)$/)) {
      this.root.addEventListener(
        RegExp.$1.replace(/^[\s\S]/, (c) => c.toLocaleLowerCase()),
        value
      );
    } else {
      if (name === 'className') {
        this.root.setAttribute('class', value);
      } else {
        this.root.setAttribute(name, value);
      }
    }
  }
   

  appendChild(component) {
    let range = document.createRange();
    range.setEnd(this.root, this.root.childNodes.length);
    range.setStart(this.root, this.root.childNodes.length);
    component[RENDER_TO_DOM](range);
  }
   */

  /**
   * 虚拟dom到实体 dom 的更新
   * @param {} range
   */
  [RENDER_TO_DOM](range) {
    this._range = range;

    // range.deleteContents();

    const root = document.createElement(this.type);

    const setAttribute = (name, value) => {
      if (name.match(/^on([\s\S]+)$/)) {
        root.addEventListener(
          RegExp.$1.replace(/^[\s\S]/, (c) => c.toLocaleLowerCase()),
          value
        );
      } else {
        if (name === 'className') {
          root.setAttribute('class', value);
        } else {
          root.setAttribute(name, value);
        }
      }
    };

    const appendChild = (component) => {
      let childRange = document.createRange();
      childRange.setEnd(root, root.childNodes.length);
      childRange.setStart(root, root.childNodes.length);
      component[RENDER_TO_DOM](childRange);
    };

    for (let name in this.props) {
      let value = this.props[name];
      setAttribute(name, value);
    }

    if (!this.vchildren)
      this.vchildren = this.children.map((child) => child.vdom);

    for (let child of this.vchildren) {
      appendChild(child);
    }

    // range.insertNode(root);

    replaceContent(range, root);
  }
  get vdom() {
    this.vchildren = this.children.map((child) => child.vdom);
    return this;
    // return {
    //   type: this.type,
    //   props: this.props,
    //   children: this.children.map((child) => child.vdom),
    // };
  }
}

class TextWrapper extends Component {
  constructor(content) {
    super(content);
    this.type = '#text';
    this.content = content;
  }
  [RENDER_TO_DOM](range) {
    this._range = range;
    const root = document.createTextNode(this.content);
    // range.deleteContents();
    // range.insertNode(this.root);
    replaceContent(range, root);
  }
  get vdom() {
    return this;
    // return {
    //   type: '#text',
    //   content: this.content,
    // };
  }
}

function replaceContent(range, node) {
  range.insertNode(node);
  range.setStartAfter(node);
  range.deleteContents();

  range.setStartBefore(node);
  range.setEndAfter(node);
}

export function createElement(type, attrs, ...children) {
  let e;
  if (typeof type === 'string') {
    e = new ElementWrapper(type);
  } else {
    e = new type();
  }

  for (let key in attrs) {
    e.setAttribute(key, attrs[key]);
  }

  const insertChildren = (children) => {
    for (let child of children) {
      if (typeof child === 'string' || typeof child === 'number') {
        child = new TextWrapper(child);
      }

      if (child === null) {
        continue;
      }

      if (typeof child === 'object' && child instanceof Array) {
        insertChildren(child);
      } else {
        e.appendChild(child);
      }
    }
  };

  insertChildren(children);

  return e;
}

export function render(component, parentElement) {
  let range = document.createRange();
  range.setStart(parentElement, 0);
  range.setEnd(parentElement, parentElement.childNodes.length);
  range.deleteContents();
  component[RENDER_TO_DOM](range);
}
